package com.magicstone.common.Licence.service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.security.Key;
import java.util.Date;
import java.util.Timer;

import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.magicstone.common.JXLSHelper;
import com.magicstone.common.Licence.domain.SysInfo;
import com.magicstone.common.Time.TimeUtil;

public class Licenser {
	//private static final String DEFAULT_CONTEXT_LOCATION = "classpath*:applicationContext.xml";
	private final static Log logger = LogFactory.getLog(Licenser.class);
	private boolean expired;
	private static Licenser me;
	private String license;
	private String lastMessage;
	
	//private GenericXmlApplicationContext ctx;
	
	private Licenser(){
		LicenceGuard guarder = new LicenceGuard();
        
        Timer timer = new Timer();
        
        timer.schedule(guarder, 1000, 3600000);
        //timer.schedule(guarder, 1000, 5000);
	}
	
	public static Licenser getInstance(){
		if(null == me){
			me = new Licenser();
		}
		logger.debug("Licencer acquired!");
		return me;
	}
	
	public static Licenser getInstance(ServletContext sc){
		Licenser o = getInstance();
		o.setLicence(JXLSHelper.pathFix(sc.getRealPath("/")) + "\\licence.txt");
		return me;
	}

	public boolean isExpired(){
		return expired;
	}
	
	public SysInfo check(){
		SysInfo siSource = load();
		SysInfo siGathered = gatherSysInfo();
		
		expired = isExpired(siSource, siGathered);
		logger.debug("Licence checking..." + expired);
		return siSource;
	}

	public void beat(SysInfo si){
		if(null == si){
			si = gatherSysInfo();
			si.setAt(SysInfo.AUTH_TYPE.DEADLINE);
			si.setApply(new Date());
			si.setDeadline(new Date());
			si.setDigest(digest(si));
		}
		Integer beated = si.getBeated();
		logger.debug("Before beated:" + beated);
		si.setBeated(null != beated ? beated + 1 : 0);
		si.setLastBeat(new Date());
		si.setDigest(digest(si));
		save(si);
		logger.debug("After beated:" + si.getBeated());
	}
	/*
	 *  Decide whether the software is expired or not.
	 *  @Param siSource information from license.
	 *  @Param siGathered information gathered dynamically.
	 */
	public static boolean isExpired(SysInfo siSource, SysInfo siGathered) {
		boolean result = true;
		if(null != siSource && null != siGathered && validate(siSource)){
			Date lastBeat = siSource.getLastBeat() != null ? siSource.getLastBeat() : new Date();
			Date apply = siSource.getApply();
			Integer beated = siSource.getBeated() != null ? siSource.getBeated() : 0;
			Date now = siGathered.getNow();
			switch(siSource.getAt()){
			case PERMERNANT:
				result = false;
				logger.debug("Permernant licence!");
				break;
			case DURATION:
				Integer duration = siSource.getDuration();
				if(null != lastBeat && null != now && null != lastBeat && lastBeat.before(now) && null != apply && apply.before(now)){
					result = beated >= (duration * 24);
				}
				break;
			case DEADLINE:
				Date deadline = siSource.getDeadline();
				if(null != deadline && null != now && null != lastBeat && lastBeat.before(now) && null != apply && apply.before(now)){
					result = deadline.before(now) || TimeUtil.getDateBefore(apply, -beated / 24).after(now);
					logger.debug(result + "=" + deadline + ":" + now);
				}
				/*
				else{
					logger.debug("null != deadline=" + (null != deadline));
					logger.debug("null != now=" + (null != now));
					logger.debug("null != lastBeat=" + (null != lastBeat));
					logger.debug("lastBeat.after(now)=" + (lastBeat.after(now)));
					logger.debug("null != apply=" + (null != apply));
					logger.debug("apply.before(now)=" + (apply.before(now)));
				}
				*/
				break;
				default:
					logger.debug("Why?");
					break;
			}
		}
		if(result){
			logger.debug("Expired?" + (null != siSource) + ":" + (null != siGathered) + ":" + validate(siSource));
		}
		return result;
	}

	public static boolean validate(SysInfo si){
		String md = digest(si);
		logger.debug(md + "vs" + si.getDigest());
		return md.equals(si.getDigest());
	}
	
	private SysInfo gatherSysInfo(){
		return new SysInfo();
	}
	
	public String generateServerInfo(){
		SysInfo si = gatherSysInfo();
		return encode(SI2String(si));
	}
	
	public boolean activate(String sn){
		String plainSN = decode(sn);
		SysInfo si = String2SI(plainSN);
		if(null != si){
			save(plainSN, license);
			check();
		}
		return expired;
	}
	
	/*
	// Decrypt licence using public key
	public String decrypt(String sn){
		return sn;
	}
	*/
	
	public static String SI2String(SysInfo si){
		String json = null;
		if(null != si){
			Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
			json = gson.toJson(si);
		}
		return json;
	}
	
	public static SysInfo String2SI(String json){
		SysInfo si = null;
		if(null != json){
			logger.debug(json);
			try{
				Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
				si = gson.fromJson(json, SysInfo.class);
			}catch(JsonSyntaxException e){
				logger.error(e.getMessage());
			}
		}
		return si;
	}

	public SysInfo load(){
		SysInfo si = null;
		try {
			File file = new File(license);
			if(file.exists()){
				String json = "";
				BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8"));
				String str;
				while((str = bufferedReader.readLine()) != null){
					json += str;
				}
				bufferedReader.close();
	    		si = String2SI(decode(json));
			}else{
				logger.debug("Licence not exists:" + license != null ? license : "null");
			}
		} catch (FileNotFoundException e) {
			logger.error(e.getMessage());
		} catch (Exception e) {
			logger.error(e.getMessage());
		}
		
		logger.debug("Load licence:" + (si != null));
		return si;
	}

	public void save(SysInfo si){
		if(null != si){
	    	String json = SI2String(si);
	    	save(json, license);
		}
	}
	
	public String encode(String json){
		DESer des = new DESer();
		return des.encrypt(json);
	}
	public String decode(String cyberJson){
		DESer des = new DESer();
		return des.decrypt(cyberJson);
	}
	
	public void save(String json, String f){
		try {
			File file = new File(f);
			logger.debug("License file:" + f);
			BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8"));
    		bufferedWriter.write(encode(json));
    		bufferedWriter.flush();
    		bufferedWriter.close();
		} catch (FileNotFoundException e) {
			logger.error(e.getMessage());
		} catch (IOException e) {
			logger.error(e.getMessage());
		}
	}
	
	public String getLicence() {
		return license;
	}

	public void setLicence(String license) {
		logger.debug("License File:" + license);
		this.license = license;
	}

	public static String digest(SysInfo si){
		String result = "";
		if(null != si){
			String mdSource = si.getDigest();
			si.setDigest("");
			String siString = SI2String(si);
			result = Encryptor.digest(siString);
			si.setDigest(mdSource);
		}
		return result;
	}

	public String getLastMessage() {
		return lastMessage;
	}

	public void setLastMessage(String lastMessage) {
		this.lastMessage = lastMessage;
	}
}
